﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;

namespace ProModel.ApplicationFramework.Silverlight
{
    #region WindowedModalControl Class

    /// <summary>
    /// The WindowedModalControl is used to render any UserControl modally inside a
    /// standard modal window frame.
    /// </summary>
    /// <remarks>
    /// When hosting a UserControl it is good practice to set at least the MinWidth and
    /// MinHeight of any control that is to be hosted and then pass these value into
    /// WindowedModalControl via the ShowModal method. If these minimum sizes are not set the
    /// WindowedModalControl will assume the minimum viewable size on it's own.
    /// </remarks>
    /// <example>
    /// 	<para><font size="2"><font size="2">publicvoidShowWindowedModal(DialogClosecallback,Panelpanel)</font></font></para>
    /// 	<para><font size="2"><font size="2">{</font></font></para>
    /// 	<para><font size="2"><font size="2">bool useCanvasPainting
    ///     =true;</font></font></para>
    /// 	<para><font size="2"><font size="2">WindowedModalControl wmc =new
    ///     WindowedModalControl(callback, MsgBoxButtons.OKCancelApply);</font></font></para>
    /// 	<para><font size="2"><font size="2">TestUserControl tc =new
    ///     TestUserControl();</font></font></para>
    /// 	<para><font size="2"><font size="2">wmc.TitleBarBrush =
    ///     Resources["DemoBrush"]asBrush;</font></font></para>
    /// 	<para><font size="2"><font size="2">wmc.FormBackgroundBrush =
    ///     Resources["GridBrush"]asBrush;</font></font></para>
    /// 	<para><font size="2"><font size="2">wmc.HostedControl = tc;</font></font></para>
    /// 	<para><font size="2"><font size="2">wmc.ShowModal(panel, useCanvasPainting,
    ///     newSize(tc.MinWidth, tc.MinHeight));</font></font></para>
    /// 	<para><font size="2"><font size="2">wmc.AllowResizing =true;</font></font></para>
    /// 	<para><font size="2"><font size="2">wmc.AllowMaximize =true;</font></font></para>
    /// 	<para><font size="2"><font size="2">wmc.TitleBarText ="It's a Working Title...and
    ///     it truncates if need be";</font></font></para>
    /// 	<para><font size="2"><font size="2">}</font></font></para>
    /// 	<para><font size="2"><font size="2"><br/>
    ///     bool OnDialogClosed(object sender, DialogExit e)</font></font></para>
    /// 	<para><font size="2"><font size="2">{</font></font></para>
    /// 	<para><font size="2"><font size="2">_tbResult.Text =
    ///     e.ToString();</font></font></para>
    /// 	<para><font size="2"><font size="2">return true;</font></font></para>
    /// 	<para><font size="2"><font size="2">}</font></font></para>
    /// </example>
    public partial class WindowedModalControl : UserControl, IModal, IDisposable 
    {
        #region Enums

        enum DragState
        {
            None,
            Move,
            SizeAll,
            ResizeLeft,
            ResizeTop,
            ResizeRight,
            ResizeBottom
        }

        #endregion

        #region Constants
        
        private const int FRAME_WIDTH = 24;
        private const int FRAME_HEIGHT = 72;
        private const int DEFAULT_WIDTH = 350;
        private const int DEFAULT_HEIGHT = 250;

        #endregion

        #region Fields/Properties

        private Size _formSize;
        private Point _formLocation;
        private Point _ptLastMousePos;
        private DialogClose _callback;
        private DragState _dragState;
        private MsgBoxButtons _buttonUse;
        private bool _layoutUpdated;

        /// <summary>Gets or sets the Brush used to render the TitleBar's background or fill.</summary>
        /// <example>
        /// 	<para><font size="2"><br/>
        ///     WindowedModalControl wmc = new WindowedModalControl(callback,
        ///     MsgBoxButtons.OKCancelApply);</font></para>
        /// 	<para><font size="2">TestUserControl tc = new TestUserControl();</font></para>
        /// 	<para><font size="2">wmc.TitleBarBrush = Resources["DemoBrush"] as
        ///     Brush;</font></para>
        /// 	<para><font size="2">wmc.HostedControl = tc;</font></para>
        /// 	<para><font size="2">wmc.ShowModal(this.LayoutRoot,
        ///     useCanvasPainting,newSize(tc.MinWidth, tc.MinHeight));</font></para>
        /// </example>
        public Brush TitleBarBrush
        {
            get 
            { 
                return TitleBar.Background;
            }
            set 
            {
                if (value != null)
                {
                    TitleBar.Background = value;
                    DragCorner.Fill = value;                    
                }
            }
        }

        /// <summary>Gets or sets the title bar image.</summary>
        public ImageSource TitleImage
        {
            get
            {
                return _imgTitleImage.Source;
            }
            set
            {
                _imgTitleImage.Source = value;
            }
        }

        /// <summary>Gets or sets the Brush used to render the Form's background or fill.</summary>
        /// <example>
        /// 	<para><font size="2"><br/>
        ///     WindowedModalControl wmc =new WindowedModalControl(callback,
        ///     MsgBoxButtons.OKCancelApply);</font></para>
        /// 	<para><font size="2">TestUserControl tc =new TestUserControl();</font></para>
        /// 	<para><font size="2">wmc.FormBackgroundBrush =
        ///     Resources["GridBrush"]asBrush;</font></para>
        /// 	<para><font size="2">wmc.HostedControl = tc;</font></para>
        /// 	<para><font size="2">wmc.ShowModal(this.LayoutRoot,
        ///     useCanvasPainting,newSize(tc.MinWidth, tc.MinHeight));</font></para>
        /// </example>
        public Brush FormBackgroundBrush
        {
            get 
            {
                return FrameBorder.Background;
            }
            set 
            {
                if (value != null)
                {
                    FrameBorder.Background = value;
                }
            }
        }

        /// <summary>
        /// Gets or sets the Brush used to render the TitleBar text's foreground
        /// color.
        /// </summary>
        /// <example>
        /// 	<para><font size="2"><font size="2">WindowedModalControl wmc =new
        ///     WindowedModalControl(callback, MsgBoxButtons.OKCancelApply);</font></font></para>
        /// 	<para><font size="2"><font size="2">TestUserControl tc =new
        ///     TestUserControl();</font></font></para>
        /// 	<para><font size="2"><font size="2">wmc.TitleBarBrush =
        ///     Resources["TextBrush"]asBrush;</font></font></para>
        /// 	<para><font size="2"><font size="2">wmc.HostedControl = tc;</font></font></para>
        /// 	<para><font size="2"><font size="2">wmc.ShowModal(this.LayoutRoot,
        ///     useCanvasPainting, new Size(tc.MinWidth, tc.MinHeight));<br/></font></font></para>
        /// </example>
        public Brush TitleBarTextBrush
        {
            get
            {
                return tblTitleBar.Foreground;
            }
            set
            {
                tblTitleBar.Foreground = value;
            }
        }

        /// <summary>Gets or sets the content of the OK button.</summary>
        public string OKText
        {
            get
            {
                return btnOK.Content.ToString();
            }
            set
            {
                btnOK.Content = value;
            }
        }


        /// <summary>Gets or sets the content of the Cancel button.</summary>
        public string CancelText
        {
            get
            {
                return btnCancel.Content.ToString();
            }
            set
            {
                btnCancel.Content = value;
            }
        }

        /// <summary>Gets or sets the content of the Apply button.</summary>
        public string ApplyText
        {
            get
            {
                return btnApply.Content.ToString();
            }
            set
            {
                btnApply.Content = value;
            }
        }

        /// <summary>
        /// Gets or sets the whether or not the maximize button is drawn on the TitleBar for
        /// the modal form.
        /// </summary>
        public bool AllowMaximize
        {
            get { return btnMaximize.IsEnabled; }
            set 
            { 
                btnMaximize.Visibility = value ?  Visibility.Visible : Visibility.Collapsed;
                stkButtonPanel.Width = value ? WindowHelpers.GetControlSize(btnMaximize).Width + WindowHelpers.GetControlSize(btnClose).Width
                    : WindowHelpers.GetControlSize(btnClose).Width;
            }
        }

        private bool _allowResizing;
        /// <summary>Gets or sets the whether or not the modal form will be resizable.</summary>
        public bool AllowResizing
        {
            get { return _allowResizing; }
            set 
            { 
                _allowResizing = value;
                Visibility visibility = value ? Visibility.Visible : Visibility.Collapsed;
                DragCorner.Visibility = visibility;
                LeftResizeRect.Visibility = visibility;
                TopResizeRect.Visibility = visibility;
                RightResizeRect.Visibility = visibility;
                BottomResizeRect.Visibility = visibility;
                FrameBorder.CornerRadius = value ? new CornerRadius(10.0, 10.0, 0.0, 10.0) : new CornerRadius(10.0, 10.0, 10.0, 10.0);
            }
        }

        /// <summary>
        /// Gets or sets the ContentIsValid property. If ContentIsInvalid is set to true the
        /// WindowedModalHost will disable the OK button (and apply if it is visible).
        /// </summary>
        /// <value>
        /// This property should be used when the hosted control's fields are in a state
        /// where an OK click should be disallowed.
        /// </value>
        public bool ContentIsValid
        {
            get
            {
                return btnOK.IsEnabled;
            }
            set
            {
                btnOK.IsEnabled = value;
                btnApply.IsEnabled = value;
            }
        }

        private WindowedModalHost _host;
        internal WindowedModalHost Host
        {
            get { return _host; }
        }

        private WindowedModal _modal;
        internal WindowedModal Modal
        {
            get { return _modal; }
        }

        private string _titleBarText;
        /// <summary>Gets or sets the modal form's TitleBar text.</summary>
        public string TitleBarText
        {
            get
            {
                return tblTitleBar.Text;
            }
            set
            {
                _titleBarText = value;
                RefreshTextMetrics();
            }
        }

        #region IModal Members

        UserControl _hostedControl;
        /// <summary>Gets or sets the control that will fill the non-windowed form space.</summary>
        /// <remarks>
        /// It is good practice to set at least the MinWidth and MinHeight of any control
        /// that is to be hosted and then pass these value into WindowedModalControl via the
        /// ShowModal method. If these minimum sizes are not set the WindowedModalControl will
        /// assume the minimum viewable size on it's own.
        /// </remarks>
        /// <example>
        /// 	<para><font size="2"><br/>
        ///     bool useCanvasPainting =true;</font></para>
        /// 	<para><font size="2">WindowedModalControl wmc =new WindowedModalControl(callback,
        ///     MsgBoxButtons.OKCancelApply);</font></para>
        /// 	<para><font size="2">TestUserControl tc =new TestUserControl();</font></para>
        /// 	<para><font size="2">wmc.HostedControl = tc;</font></para>
        /// 	<para><font size="2">wmc.ShowModal(this.LayoutRoot,
        ///     useCanvasPainting,newSize(tc.MinWidth, tc.MinHeight));</font></para>
        /// </example>
        public UserControl HostedControl
        {
            get
            {
                return _hostedControl;
            }
            set
            {
                _hostedControl = value;

                if (_hostedControl != null)
                {
                    _userPanel.Children.Clear();
                    _userPanel.Children.Add(_hostedControl);
                }
            }
        }

        #endregion

        #endregion

        #region Construction

        public WindowedModalControl(DialogClose callback, MsgBoxButtons buttons)
        {
            InitializeComponent();
            InitWindowedModal(callback, buttons);
        }

        private void InitWindowedModal(DialogClose callback, MsgBoxButtons buttons)
        {
            System.Windows.Application.Current.Host.Content.Resized += new EventHandler(Content_Resized);

            _modal = new WindowedModal();
            _modal.PopupCreated += new EventHandler(PopupCreated);
            LayoutUpdated += new EventHandler(WindowedModalControl_LayoutUpdated);
            _callback = callback;
            _buttonUse = buttons;
            btnApply.Tag = DialogExit.Apply;
            btnCancel.Tag = DialogExit.Cancel;
            btnClose.Tag = DialogExit.Cancel;
            btnOK.Tag = DialogExit.OK;

            btnOK.Visibility = Visibility.Visible;
            btnCancel.Visibility = Visibility.Visible;
            btnApply.Visibility = Visibility.Collapsed;

            switch (_buttonUse)
            {
                case MsgBoxButtons.OK:
                    btnCancel.Visibility = Visibility.Collapsed;
                    break;
                case MsgBoxButtons.Cancel:
                    btnOK.Visibility = Visibility.Collapsed;
                    break;
                case MsgBoxButtons.OKCancel:
                    break;
                case MsgBoxButtons.OKCancelApply:
                    btnApply.Visibility = Visibility.Visible;
                    break;
                default:
                    break;
            }
        }

        #endregion

        #region Events

        void PopupCreated(object sender, EventArgs e)
        {
            _host = _modal.Host as WindowedModalHost;

            WireEvents();
        }

        private void WireEvents()
        {
            Host.BackgroundCanvas.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown);
            Host.BackgroundCanvas.MouseLeftButtonUp += new MouseButtonEventHandler(OnMouseLeftButtonUp);
            Host.BackgroundCanvas.MouseMove += new MouseEventHandler(OnMouseMove);    
            Host.BackgroundCanvas.MouseLeave += new MouseEventHandler(OnMouseLeave);
        }

        private void UnwireEvents()
        {
            Host.BackgroundCanvas.MouseLeftButtonDown -= new MouseButtonEventHandler(OnMouseLeftButtonDown);
            Host.BackgroundCanvas.MouseLeftButtonUp -= new MouseButtonEventHandler(OnMouseLeftButtonUp);
            Host.BackgroundCanvas.MouseMove -= new MouseEventHandler(OnMouseMove);
            Host.BackgroundCanvas.MouseLeave -= new MouseEventHandler(OnMouseLeave);
        }

        private void btnClick(object sender, RoutedEventArgs e)
        {
            Button obt = (Button)sender;
            DialogExit dialogResult = (DialogExit)obt.Tag;
            
            bool exit = false;

            // if not subscribers than simply exit
            if (_callback == null)
            {
                exit = true;
            }
            // if subscribers than check for apply click
            else
            {
                exit = (dialogResult == DialogExit.Apply) ? false : true;

                if (exit)
                {
                    exit = _callback(this, dialogResult);
                }
                else _callback(this, dialogResult);
            }

            if (exit)
            {
                _modal.HideModal();
            }
        }

        private void btnMaximize_Click(object sender, RoutedEventArgs e)
        {
            if (Host.RestoreState == RestoreState.Maximized)
            {
                SetWindowToUnmaximized();
            }
            else
            {
                SetWindowToMaximized();
            }
        }

        private void Content_Resized(object sender, EventArgs e)
        {
            if (Host.RestoreState == RestoreState.Maximized)
            {
                #region Window Size

                SetWindowSize(System.Windows.Application.Current.Host.Content.ActualWidth, System.Windows.Application.Current.Host.Content.ActualHeight);

                #endregion

                #region Window Position

                SetWindowLocation(0.0, 0.0);

                #endregion
            }
        }

        bool _layoutPush = false;
        void WindowedModalControl_LayoutUpdated(object sender, EventArgs e)
        {
            _layoutUpdated = true;
            if (!_layoutPush)
            {
                _layoutPush = true;
                RefreshTextMetrics();
            }
            else
            {
                _layoutPush = false;
            }
        }

        private void RefreshTextMetrics()
        {
            if (_layoutUpdated && ! _modal.IsClosing)
            {
                double buffer = 5.0;
                tblTitleBar.Text = _titleBarText;
                
                GeneralTransform gt = this.TransformToVisual(tblTitleBar);
                Point tblPos = gt.Transform(new Point(0, 0));

                GeneralTransform gt2 = this.TransformToVisual(stkButtonPanel);
                Point stkPos = gt2.Transform(new Point(0, 0));

                double tblLeft = tblPos.X * -1;
                double stkLeft = stkPos.X * -1;
                double roomForText = (stkLeft - tblLeft) - buffer;

                // is the text longer than the space available?  If so truncate it
                if (tblTitleBar.ActualWidth > roomForText && roomForText > 0)
                {                    
                    do
                    {
                        tblTitleBar.Text = tblTitleBar.Text.Remove(tblTitleBar.Text.Length - 1, 1);
                    } while (tblTitleBar.ActualWidth > roomForText);

                    if (tblTitleBar.Text.Length > 3)
                    {
                        // remove 3 more
                        tblTitleBar.Text = tblTitleBar.Text.Remove(tblTitleBar.Text.Length - 3, 3);
                        // now for the money shot
                        tblTitleBar.Text += "...";                        
                    }
                }

                if (roomForText > 0)
                {
                    //set the textblock to be as large as it can be
                    tblTitleBar.Width = roomForText;
                }
            }
        }

        #endregion

        #region Window Methods

        /// <returns><para></para></returns>
        /// <summary>
        /// 	<para><font color="green" size="2"><font color="green" size="2">Causes the hosted
        ///     UserControl to be displayed modally in a framed window.</font></font></para>
        /// </summary>
        /// <example>
        /// 	<para>publicvoidShowWindowedModal(DialogClose callback, Panel panel)</para>
        /// 	<para>{</para>
        /// 	<para>bool useCanvasPainting =true;</para>
        /// 	<para>WindowedModalControl wmc =new WindowedModalControl(callback,
        ///     MsgBoxButtons.OKCancelApply);</para>
        /// 	<para>TestUserControl tc = new TestUserControl();</para>
        /// 	<para>wmc.HostedControl = tc;</para>
        /// 	<para>wmc.ShowModal(panel, useCanvasPainting);</para>
        /// 	<para>wmc.AllowResizing = true;</para>
        /// 	<para>wmc.AllowMaximize = true;</para>
        /// 	<para>wmc.TitleBarText = "It's a Working Title...and it truncates if need
        ///     be";</para>
        /// 	<para>}</para>
        /// </example>
        /// <param name="parent">
        /// 	<para><font color="green" size="2"><font color="green" size="2">The
        ///     FrameworkElement that parents the hosted UserControl.</font></font></para>
        /// </param>
        /// <param name="useCanvasPainting">
        /// 	<para><font color="green" size="2"><font color="green" size="2">Causes the browser
        ///     area not covered by the modal to be painted over with a v</font></font><font color="green" size="2"><font color="green" size="2">sible
        ///     transparency.</font></font></para>
        /// </param>
        public bool ShowModal(Panel parent, bool useCanvasPainting)
        {
            bool success = _modal.ShowModal(parent, this, useCanvasPainting);

            if (success)
            {
                Size sz = GetHostedControlsMinimumSize();
                SetInnerControlBounds(sz);
            }

            return success;
        }

        /// <returns><para></para></returns>
        /// <summary>
        /// 	<para><font color="green" size="2"><font color="green" size="2">Causes the hosted
        ///     UserControl to be displayed modally in a framed window.</font></font></para>
        /// </summary>
        /// <example>
        /// 	<para>publicvoidShowWindowedModal(DialogClose callback,Panel panel)</para>
        /// 	<para>{</para>
        /// 	<para>booluseCanvasPainting =true;</para>
        /// 	<para>WindowedModalControl wmc =new WindowedModalControl(callback,
        ///     MsgBoxButtons.OKCancelApply);</para>
        /// 	<para>TestUserControltc =new TestUserControl();</para>
        /// 	<para>wmc.HostedControl = tc;</para>
        /// 	<para>wmc.ShowModal(panel, useCanvasPainting, newSize(tc.MinWidth,
        ///     tc.MinHeight));</para>
        /// 	<para>wmc.AllowResizing =true;</para>
        /// 	<para>wmc.AllowMaximize =true;</para>
        /// 	<para>wmc.TitleBarText ="It's a Working Title...and it truncates if need
        ///     be";</para>
        /// 	<para>}</para>
        /// 	<para></para>
        /// </example>
        /// <param name="parent">
        /// 	<para><font color="green" size="2"><font color="green" size="2">The
        ///     FrameworkElement that parents the hosted UserControl.</font></font></para>
        /// </param>
        /// <param name="useCanvasPainting">
        /// 	<para><font color="green" size="2"><font color="green" size="2">Causes the browser
        ///     area not covered by the modal to be painted over with a v</font></font><font color="green" size="2"><font color="green" size="2">sible
        ///     transparency.</font></font></para>
        /// </param>
        /// <param name="minSize">
        /// 	<para><font color="green" size="2"><font color="green" size="2">Specifies the
        ///     minimum height and width to be used with the hosted</font></font>
        /// 		<font color="green" size="2"><font color="green" size="2">UserControl. Note, this will be the
        ///     starting display size for the hosted control.</font></font></para>
        /// </param>
        public bool ShowModal(Panel parent, bool useCanvasPainting, Size minSize)
        {
            bool success = _modal.ShowModal(parent, this, useCanvasPainting);

            if (success)
            {
                SetInnerControlBounds(minSize);
            }

            return success;
        }

        private double GetHostsMinimumWidth()
        {
            double retValue = 0.0;

            switch (_buttonUse)
            {
                case MsgBoxButtons.OK:
                    {
                        retValue = btnOK.MinWidth;
                    } break;
                case MsgBoxButtons.Cancel:
                    {
                        retValue = btnCancel.MinWidth;
                    } break;
                case MsgBoxButtons.OKCancel:
                    {
                        retValue = btnOK.MinWidth + btnCancel.MinWidth;
                    } break;
                case MsgBoxButtons.OKCancelApply:
                    {
                        retValue = btnOK.MinWidth +
                            btnCancel.MinWidth  +
                            btnApply.MinWidth ;
                    } break;
                default:
                    break;
            }

            retValue += FRAME_WIDTH;
            return retValue;
        }

        private double GetHostsMinimumHeight()
        {
            double retValue = 0.0;
            retValue = WindowHelpers.GetFrameworkElementDisplaySize(btnOK).Height;
            retValue += FRAME_HEIGHT;
            return retValue;
        }

        private Size GetHostedControlsMinimumSize()
        {
            Size retSz = new Size();

            // first try to get min sizes if they are set
            retSz.Width = HostedControl.MinWidth;
            retSz.Height = HostedControl.MinHeight;

            // next check actual rendered sizes if needed
            if (IsInvalidDimension(retSz.Width))
            {
                retSz.Width = WindowHelpers.GetControlSize(HostedControl).Width;
            }

            if (IsInvalidDimension(retSz.Height))
            {
                retSz.Height = WindowHelpers.GetControlSize(HostedControl).Height;
            }

            // next set some defaults
            if (IsInvalidDimension(retSz.Width))
            {
                retSz.Width = DEFAULT_WIDTH;
            }

            if (IsInvalidDimension(retSz.Height))
            {
                retSz.Height = DEFAULT_HEIGHT;
            }

            return retSz;
        }

        private static bool IsInvalidDimension(double dim)
        {
            return double.IsNaN(dim) || dim <= 0.0;
        }

        private void SetInnerControlBounds(Size minSize)
        {            
            double minWidth = GetHostsMinimumWidth();
            double minHeight = GetHostsMinimumHeight();

            // rect frame width pixels
            this.Width = (IsInvalidDimension(minSize.Width)) || minSize.Width < minWidth ? minWidth : minSize.Width + FRAME_WIDTH;
            // rect frame height pixels
            this.Height = (IsInvalidDimension(minSize.Height)) || minSize.Height < minHeight ? minHeight : minSize.Height + FRAME_HEIGHT; 

            this.MinWidth = this.Width;
            this.MinHeight = this.Height;
        }

        private void SetWindowToMaximized()
        {
            #region Window Size

            _formSize = new Size(LayoutRoot.ActualWidth, LayoutRoot.ActualHeight);
            SetWindowSize(System.Windows.Application.Current.Host.Content.ActualWidth, System.Windows.Application.Current.Host.Content.ActualHeight);

            #endregion

            #region Window Position

            _formLocation = new Point((double)this.GetValue(Canvas.LeftProperty), (double)this.GetValue(Canvas.TopProperty));
            SetWindowLocation(0.0, 0.0);

            #endregion

            Host.RestoreState = RestoreState.Maximized;
        }

        private void SetWindowLocation(double x, double y)
        {
//            System.Windows.MessageBox.Show(string.Format("left: {0} top: {1}", dblX, dblY));
            double proposedX = x;
            double proposedY = y;

            if (x + this.Width > System.Windows.Application.Current.Host.Content.ActualWidth)
            {
                double newX = proposedX - ((x + this.Width) - System.Windows.Application.Current.Host.Content.ActualWidth);
                proposedX = (newX > 0) ? newX : 0;
            }

            if (y + this.Height > System.Windows.Application.Current.Host.Content.ActualHeight)
            {
                double newY = proposedY - ((y + this.Height) - System.Windows.Application.Current.Host.Content.ActualHeight);
                proposedY = (newY > 0) ? newY : 0;
            }

            this.SetValue(Canvas.LeftProperty, proposedX);
            this.SetValue(Canvas.TopProperty, proposedY);
        }

        private void SetWindowToUnmaximized()
        {
            Host.RestoreState = RestoreState.Unmaximized;

            #region Window Size

            SetWindowSize(_formSize.Width, _formSize.Height);
            #endregion

            #region Window Position

            SetWindowLocation(_formLocation.X, _formLocation.Y);

            #endregion
        }

        private void SetWindowSize(double width, double height)
        {
            this.Width = width;
            this.Height = height;
        }

        #endregion

        #region Mouse Stuff

        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            _ptLastMousePos = new Point(e.GetPosition(null).X, e.GetPosition(null).Y);
            if (HitTest(DragCorner))
            {
                e.Handled = true;
                _dragState = DragState.SizeAll;
            }
            else if (HitTest(LeftResizeRect))
            {
                e.Handled = true;
                _dragState = DragState.ResizeLeft;
            }
            else if (HitTest(TopResizeRect))
            {
                e.Handled = true;
                _dragState = DragState.ResizeTop;
            }
            else if (HitTest(RightResizeRect))
            {
                e.Handled = true;
                _dragState = DragState.ResizeRight;
            }
            else if (HitTest(BottomResizeRect))
            {
                e.Handled = true;
                _dragState = DragState.ResizeBottom;
            }
            else if (HitTest(TitleBar))
            {
                bool bMouseCapturing = this.CaptureMouse();

                if (bMouseCapturing) _dragState = DragState.Move;
                _ptLastMousePos = e.GetPosition(null);
            }
        }

        private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            FreeMouse();
        }

        private void FreeMouse()
        {
            if (_dragState == DragState.Move)
            {
                this.ReleaseMouseCapture();
                _ptLastMousePos = new Point();
            }

            _dragState = DragState.None;
        }
        
        private void OnMouseMove(object sender, MouseEventArgs e)
        {            
            //coordinates.Text = string.Format("Coordinates: {0},{1}", e.GetPosition(this).X.ToString(), e.GetPosition(this).Y.ToString());
            //offset.Text = string.Format("Offset: {0},{1}", e.GetPosition(_modalControl.OHost).X.ToString(), e.GetPosition(_modalControl.OHost).Y.ToString());

            Point position = e.GetPosition(null);

            if (_dragState == DragState.Move && Host.RestoreState != RestoreState.Maximized)
            {
                #region Move Window

                double dblX = (double)this.GetValue(Canvas.LeftProperty)
                    + position.X - _ptLastMousePos.X;

                double dblY = (double)this.GetValue(Canvas.TopProperty)
                    + position.Y - _ptLastMousePos.Y;

                if (dblX > 0 && dblX + this.ActualWidth < Host.ActualWidth)
                    this.SetValue(Canvas.LeftProperty, dblX);

                if (dblY > 0 && dblY + this.ActualHeight < Host.ActualHeight)
                    this.SetValue(Canvas.TopProperty, dblY);

                #endregion
            }
            else if (_dragState != DragState.None && 
                     _dragState != DragState.Move && 
                     Host.RestoreState != RestoreState.Maximized)
            {
                double xOffset = 0.0;
                double yOffset = 0.0;    

                switch (_dragState)
                {
                    case DragState.SizeAll:
                        {
                            #region SizeAll

                            xOffset = WindowHelpers.GetControlSize(this).Width + (position.X - _ptLastMousePos.X);
                            yOffset = WindowHelpers.GetControlSize(this).Height + (position.Y - _ptLastMousePos.Y);

                            if (xOffset > this.MinWidth)
                            {
                                this.SetValue(Canvas.WidthProperty, xOffset);
                            }

                            if (yOffset > this.MinHeight)
                            {
                                this.SetValue(Canvas.HeightProperty, yOffset);
                            }

                            #endregion
                        } break;
                    case DragState.ResizeLeft:
                        {
                            #region ResizeLeft

                            double offset = position.X - _ptLastMousePos.X;
                            xOffset = (double)this.GetValue(Canvas.LeftProperty) + offset;

                            double newWidth = WindowHelpers.GetControlSize(this).Width + offset * -1;

                            if (newWidth < Host.ActualWidth && newWidth > this.MinWidth)
                            {
                                this.SetValue(Canvas.LeftProperty, xOffset);
                                this.SetValue(Canvas.WidthProperty, newWidth);
                            }

                            #endregion
                        } break;
                    case DragState.ResizeTop:
                        {
                            #region ResizeTop

                            double offset = position.Y - _ptLastMousePos.Y;
                            yOffset = (double)this.GetValue(Canvas.TopProperty) + offset;
                            double newHeight = WindowHelpers.GetControlSize(this).Height + offset * -1;

                            if (newHeight < Host.ActualHeight && newHeight > this.MinHeight)
                            {
                                this.SetValue(Canvas.TopProperty, yOffset);
                                this.SetValue(Canvas.HeightProperty, newHeight);
                            }

                            #endregion
                        } break;
                    case DragState.ResizeRight:
                        {
                            #region ResizeRight

                            double offset = position.X - _ptLastMousePos.X;
                            xOffset = (double)this.GetValue(Canvas.WidthProperty) + offset;
                            double newWidth = WindowHelpers.GetControlSize(this).Width + offset;

                            if (newWidth < Host.ActualWidth && newWidth > this.MinWidth)
                            {
                                this.SetValue(Canvas.WidthProperty, newWidth);
                            }

                            #endregion
                        } break;
                    case DragState.ResizeBottom:
                        {
                            #region ResizeBottom

                            yOffset = position.Y - _ptLastMousePos.Y;
                            double newHeight = WindowHelpers.GetControlSize(this).Height + yOffset;

                            if (newHeight < Host.ActualHeight && newHeight > this.MinHeight)
                            {
                                this.SetValue(Canvas.HeightProperty, newHeight);
                            }

                            #endregion
                        } break;
                    default:
                        break;
                }
            }

            _ptLastMousePos = position;
        }

        // drags that leave the browser are now automatically terminated
        private void OnMouseLeave(object sender, MouseEventArgs e)
        {
            if (Host.RestoreState != RestoreState.Maximized)
            {
                FreeMouse();
            }
        }

        private bool HitTest(UIElement element)
        {
            bool retItem = false;
            List<UIElement> hits = new List<UIElement>();

            try
            {
                hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(_ptLastMousePos, this) as List<UIElement>;
            }
            catch { }

            if (hits.Count > 0)
            {
                foreach (UIElement e in hits)
                {
                    if (e == element)
                    {
                        retItem = true;
                        break;
                    }
                }
            }

            return retItem;
        }

        #endregion

        #region IDisposable Members

        public virtual void Dispose()
        {
            UnwireEvents();
        }

        #endregion
    }

    #endregion

    #region WindowedModal Class

    /// <summary>For internal use only.</summary>
    internal class WindowedModal : Modal
    {
        #region Method Declarations

        protected override void SetHost(IModal ctrl, bool useCanvasPainting)
        {
            _host = new WindowedModalHost(ctrl, useCanvasPainting);
        }

        #endregion
    }

    #endregion

    #region WindowedModalHost Class

    /// <summary>For internal use only.</summary>
    internal class WindowedModalHost : ModalHost
    {
        #region Fields/Properties

        internal RestoreState _restoreState = RestoreState.Unmaximized;
        internal RestoreState RestoreState
        {
            get { return _restoreState; }
            set { _restoreState = value; }
        }

        #endregion

        #region Construction

        public WindowedModalHost(IModal control, bool useCanvasPainting)
            : base(control, useCanvasPainting)
        {

        }

        #endregion

        #region Window Methods

        public override void CentreWindow()
        {
            double dblX = 0.0, dblY = 0.0;
            UserControl modal = _modal as UserControl;

            if (RestoreState == RestoreState.Unmaximized)
            {
                // width
                if (_modal != null && !double.IsNaN(WindowHelpers.GetControlSize(modal).Width) &&
                    !double.IsNaN(Application.Current.Host.Content.ActualWidth))
                {
                    dblX = ((Application.Current.Host.Content.ActualWidth - WindowHelpers.GetControlSize(modal).Width) / 2);
                    modal.SetValue(Canvas.LeftProperty, dblX);
                }

                // height
                if (_modal != null && !double.IsNaN(WindowHelpers.GetControlSize(modal).Height) &&
                    !double.IsNaN(Application.Current.Host.Content.ActualHeight))
                {
                    dblY = ((Application.Current.Host.Content.ActualHeight - WindowHelpers.GetControlSize(modal).Height) / 2);
                    modal.SetValue(Canvas.TopProperty, dblY);
                }
            }

            //System.Windows.MessageBox.Show(string.Format("left: {0} top: {1}", dblX, dblY));
        }

        #endregion

        #region Mouse Stuff

        protected override void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            bool doMouseCapture = true;

            List<UIElement> hits;
            hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(e.GetPosition(this), this) as List<UIElement>;
            if (hits.Contains(_modal.HostedControl))
            {
                doMouseCapture = false;
            }

            if (doMouseCapture)
            {
                _bMouseCapturing = _modal.HostedControl.CaptureMouse();
                _oLastMousePos = e.GetPosition(null);
            }
        }

        protected override void OnMouseMove(object sender, MouseEventArgs e)
        {
            // prevent the base class from taking action
        }

        #endregion
    }

    #endregion
}
